/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
**     of its contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtWidgets>
#include <QtOpenGL>

#include "glwidget.h"

GLWidget::GLWidget(QWidget *parent, QGLWidget *shareWidget)
    : QGLWidget(parent, shareWidget)
{
    clearColor = Qt::black;

    modelViewMatrix.setToIdentity();
    projectionMatrix.setToIdentity();
    cameraPos.setX(0.0f);
    cameraPos.setY(0.0f);
    cameraPos.setZ(-3.0f);


    //initialize pbuffer
    QGLFormat pbufferFormat = format();
    pbufferFormat.setSampleBuffers(false);
    pGLBuffer = new QGLPixelBuffer(QSize(512, 512), pbufferFormat, this);



    xRot = 0;
    yRot = 0;
    zRot = 0;
    program = 0;
    pMesh = NULL;
    renderToBuffer = false;
    pGLFbo = NULL;

    logTextStream = NULL;
    bufferFileName = "buffer.pfm";

}

GLWidget::~GLWidget()
{
    if(pMesh)
        delete pMesh;

    delete pGLBuffer;
    delete vshader;
    delete fshader;
    if(pGLFbo)
        delete pGLFbo;

}

QSize GLWidget::minimumSizeHint() const
{
    return QSize(256, 256);
}

QSize GLWidget::sizeHint() const
{
    return QSize(256, 256);
}

void GLWidget::renderToPBuffer()
{
    renderToBuffer = true;
}

void GLWidget::renderToPBuffer(QString fileName)
{
    renderToBuffer = true;
    bufferFileName = fileName;
}


void GLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
    xRot += xAngle;
    yRot += yAngle;
    zRot += zAngle;
    updateGL();
}

void GLWidget::setClearColor(const QColor &color)
{
    clearColor = color;
    updateGL();
}

void GLWidget::initializePBuffer()
{
/*    pbuffer->makeCurrent();

    cubeTexture = bindTexture(QImage(":res/cubelogo.png"));

    initCommon();

    // generate a texture that has the same size/format as the pbuffer
    dynamicTexture = pbuffer->generateDynamicTexture();

    // bind the dynamic texture to the pbuffer - this is a no-op under X11
    hasDynamicTextureUpdate = pbuffer->bindToDynamicTexture(dynamicTexture);
    makeCurrent();
*/
}

void GLWidget::drawPBuffer()
{

}

void GLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    initializePBuffer();

    makeObject();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
//#ifdef GL_TEXTURE_2D
    glEnable(GL_TEXTURE_2D);
//#endif

#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_NORMAL_ATTRIBUTE 1
#define PROGRAM_DIFFCOLOR_ATTRIBUTE       2
//#define PROGRAM_TEXCOORD_ATTRIBUTE 1

    vshader = new QGLShader(QGLShader::Vertex, this);
    vshader->compileSourceFile("face.vert");
    fshader = new QGLShader(QGLShader::Fragment, this);
    fshader->compileSourceFile("face.frag");
    program = new QGLShaderProgram(this);
    program->addShader(vshader);
    program->addShader(fshader);

    program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
//    program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
    program->bindAttributeLocation("normal", PROGRAM_NORMAL_ATTRIBUTE);
    program->bindAttributeLocation("diffColor",PROGRAM_DIFFCOLOR_ATTRIBUTE);
    program->link();
//    program->setUniformValue("texture", 0);
}

void GLWidget::paintGL()
{

    if(renderToBuffer)
    {
        if(logTextStream)
        {
            *logTextStream<<"create fbo\n";
            logTextStream->flush();
        }
//        pGLBuffer->makeCurrent();

        pGLFbo = new GLFrameBufferObject(this->width(),this->height(), 3);
        pGLFbo->SetLogTextStreamObj(logTextStream);
        pGLFbo->CreateFbo();
        pGLFbo->CheckFboLimits();
        pGLFbo->BindFbo();
    }
    else
    {
        this->makeCurrent();
    }

    qglClearColor(clearColor);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    projectionMatrix.setToIdentity();
    projectionMatrix.perspective(45.0f, (float)this->width()/(float)this->height(), 0.001, 1000);

    modelViewMatrix.setToIdentity();
    modelViewMatrix.lookAt(cameraPos, QVector3D(0,0,0), QVector3D(0.0, 1.0, 0.0));

    modelViewMatrix.rotate(-xRot / 16.0f, 1.0f, 0.0f, 0.0f);
    modelViewMatrix.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
    modelViewMatrix.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);


//    modelViewMatrix.rotate(-18.0f, 0.0f, 1.0f, 0.0f);
//    modelViewMatrix.rotate(-90.0f, 0.0f, 0.0f, 1.0f);


/*    glLineWidth(1);
    glColor3f(1.0, 0.0, 0.0);
    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1, 0, 0);
    glEnd();

    glLineWidth(3);
    glColor3f(0.0, 1.0, 0.0);
    glBegin(GL_LINES);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0, 1, 0);
    glEnd();

    glLineWidth(9);
    glColor3f(0.0, 0.0, 1.0);
    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0, 0, 1);
    glEnd();
*/
    program->bind();

    program->setUniformValue("modelViewMatrix", modelViewMatrix);
    program->setUniformValue("projMatrix",projectionMatrix);

    program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
    program->enableAttributeArray(PROGRAM_NORMAL_ATTRIBUTE);
    program->enableAttributeArray(PROGRAM_DIFFCOLOR_ATTRIBUTE);
//    program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
    program->setAttributeArray
       (PROGRAM_VERTEX_ATTRIBUTE, pMesh->VerticesPointer(),3);
//    program->setAttributeArray
//        (PROGRAM_TEXCOORD_ATTRIBUTE, texCoords.constData());
    program->setAttributeArray(PROGRAM_NORMAL_ATTRIBUTE, pMesh->VertexNormalsPointer(),3);
    program->setAttributeArray(PROGRAM_DIFFCOLOR_ATTRIBUTE, pMesh->VerticesColorPointer(), 3);

    glDrawElements(GL_TRIANGLES, pMesh->NumTris()*3, GL_UNSIGNED_INT, pMesh->FacesIndicePointer());

//    for (int i = 0; i < 6; ++i) {
//        glBindTexture(GL_TEXTURE_2D, textures[i]);
//        glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);          
//    }

    program->release();

    if(renderToBuffer)
    {
        pGLFbo->ReadBuffer();
        pGLFbo->SaveBufferToFile(bufferFileName);
        pGLFbo->ReleaseFbo();
        delete pGLFbo;
        pGLFbo = NULL;
        renderToBuffer = false;
//        QImage image = pGLBuffer->toImage();
//        image.save("1.jpg");
    }


}

void GLWidget::resizeGL(int width, int height)
{
  //  int side = qMin(width, height);
 //   glViewport((width - side) / 2, (height - side) / 2, side, side);
    glViewport(0,0,width,height);

//    pGLFbo->BindFbo();
//    glViewport(0,0,width,height);
//    pGLFbo->ReleaseFbo();


}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();

    if (event->buttons() & Qt::LeftButton) {
        rotateBy(8 * dy, 8 * dx, 0);
    } else if (event->buttons() & Qt::RightButton) {
        rotateBy(8 * dy, 0, 8 * dx);
    }
    lastPos = event->pos();
}

void GLWidget::mouseReleaseEvent(QMouseEvent * /* event */)
{
    emit clicked();
}

void GLWidget::makeObject()
{
/*    static const int coords[6][4][3] = {
        { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } },
        { { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } },
        { { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } },
        { { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } },
        { { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } },
        { { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } }
    };
    QString str = QApplication::applicationDirPath ();
    for (int j=0; j < 6; ++j) {
        textures[j] = bindTexture
            (QPixmap(str+QString("/images/side%1.png").arg(j + 1)), GL_TEXTURE_2D);
    }

    for (int i = 0; i < 6; ++i) {
        for (int j = 0; j < 4; ++j) {
            texCoords.append
                (QVector2D(j == 0 || j == 3, j == 0 || j == 1));
            vertices.append
                (QVector3D(0.2 * coords[i][j][0], 0.2 * coords[i][j][1],
                           0.2 * coords[i][j][2]));
        }
    }
*/
    pMesh = new Mesh();
    pMesh->readPly("templateface.ply", 0);//("face_mesh_000306.ply", 0);"bun_zipper_res4.ply", 1
    pMesh->Normalize();
    QMatrix4x4 rotMat;
    rotMat.setToIdentity();
    rotMat.rotate(-18.0f, 0.0f, 1.0f, 0.0f);
    rotMat.rotate(-90.0f, 0.0f, 0.0f, 1.0f);
    pMesh->Rotate(rotMat);

    pMesh->ComputeNormals();

}
